#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os
import stat
import time

from common.common import execute_cmd, execute_cmd_list
from common.const import SubJobStatusEnum
from sqlserver import log
from sqlserver.commons.const import SQLServerProgressFileType
from sqlserver.commons.const import SqlCmd, ProgressConst, ParamConstant
from sqlserver.commons.sqlserver_parse_param import VDIAPINAME
from sqlserver.sqlserver_restore_instance import SQLServerRestoreInstance


class SQLServerRestoreClusterInstance:
    def __init__(self, p_id, job_id, sub_job_id, json_param):
        self.my_base = SQLServerRestoreInstance(p_id, job_id, sub_job_id, json_param)

    @staticmethod
    def write_to_ps_script(cmd):
        tmp_script = os.path.join(ParamConstant.PARAM_FILE_PATH, "temp2.ps1")
        flags = os.O_WRONLY | os.O_CREAT
        modes = stat.S_IWUSR | stat.S_IRUSR
        with os.fdopen(os.open(tmp_script, flags, modes), 'w+') as out_file:
            out_file.write(cmd)

    def exec_pw_shell(self, shell_cmd):
        try:
            self.write_to_ps_script(shell_cmd)
        except Exception as e_info:
            return False, ''
        tmp_script = os.path.join(ParamConstant.PARAM_FILE_PATH, "temp2.ps1")
        cmds = f'{VDIAPINAME.ps_loc} {tmp_script}'
        ret, std_out, std_err = execute_cmd(cmds)
        if int(ret) != 0:
            log.error(f"Ps cmd exec failed, msg={std_err}")
            return False, ''
        try:
            os.remove(tmp_script)
        except Exception as err:
            log.error(f"Delete file temp2.ps1 failed: {err}!")
        return True, std_out.split('\n')

    def exec_pw_shell_contains_cmd_list(self, shell_cmd):
        try:
            self.write_to_ps_script(shell_cmd)
        except Exception as e_info:
            return False, ''
        tmp_script = os.path.join(ParamConstant.PARAM_FILE_PATH, "temp2.ps1")
        cmds = f'{VDIAPINAME.ps_loc} {tmp_script}'
        if len(cmds.split("|")) > 1:
            cmd_list = cmds.split("|")
            ret, std_out, std_err = execute_cmd_list(cmd_list)
        else:
            ret, std_out, std_err = execute_cmd(cmds)
        if int(ret) != 0:
            log.error(f"Ps cmd exec failed, msg={std_err}")
            return False, ''
        try:
            os.remove(tmp_script)
        except Exception as err:
            log.error(f"Delete file temp2.ps1 failed: {err}!")
        return True, std_out.split('\n')

    def get_physical_disk_name(self):
        """
        获取物理盘信息
        :return:
        """
        cmd = "Get-ClusterResource -Name \"*Disk*\" | Format-List"
        flag, std_out = self.exec_pw_shell_contains_cmd_list(cmd)
        log.info(f"flag:{flag}, std_out:{std_out}")
        if not flag:
            return False, ""
        idxs = 0
        for list_mbr in std_out:
            if list_mbr.find("SQL Server") != -1 and \
                    std_out[idxs - 1].find("Online") != -1:
                ret_list = std_out[idxs - 2].split(':')
                return True, ret_list[1].strip()
            idxs += 1
        return True, ""

    def maintenance_mode_on(self, flags):
        """
        切换维护模式
        :param flags: True,开启维护模式，False 关闭维护模式
        :return:
        """
        ret_flags, name = self.get_physical_disk_name()
        log.info(f"ret_flags:{ret_flags},name:{name}")
        if not ret_flags:
            return False
        # 有没有物理盘的场景
        if not name:
            return True
        if flags:
            cmds = f'{SqlCmd.SUSPEND_RESOURCE} \"{name}\"'
        else:
            cmds = f'{SqlCmd.RESUME_RESOURCE} \"{name}\"'
        flags, std_out = self.exec_pw_shell(cmds)
        log.info(f"cmds:{cmds}, flags:{flags},std_out:{std_out}")
        if not flags:
            log.error(f'Turn mode Failed, ErrMsg={std_out}')
            return False
        return True

    def start_cluster_resources(self):
        """
        wsfc集群恢复后，SQL Server和SQL Server Agent可能处于failed状态
        """
        restart_service = False
        resources_name = ["SQL Server", "SQL Server Agent"]
        for resource in resources_name:
            query_cmd = f"Get-ClusterResource -Name \"{resource}\" | Select-Object State"
            flag, std_out = self.exec_pw_shell(query_cmd)
            log.info(f"resource:{resource}, flags:{flag},std_out:{std_out}")
            if not flag:
                continue
            if len(std_out) >= 4 and str(std_out[3]).strip().find("Online") != -1:
                continue
            flag, out = self.exec_pw_shell(f"Start-ClusterResource -Name \"{resource}\"")
            log.info(f"start cluster resource:{resource}, flag,:{flag},std_out:{out}")
            if not flag:
                return False
            restart_service = True
        if restart_service:
            flag, out = self.exec_pw_shell(f"Stop-Service -Name MSSQLSERVER -Force")
            log.info(f"stop service:MSSQLSERVER, flag,:{flag},std_out:{out}")
            time.sleep(10)
            flag, out = self.exec_pw_shell(f"Start-Service -Name MSSQLSERVER")
            log.info(f"start service:MSSQLSERVER, flag,:{flag},std_out:{out}")
            self.exec_pw_shell(f"Start-ClusterResource -Name \"SQL Server\"")
        return True

    def allow_restore_in_local_node(self):
        return self.my_base.allow_restore_in_local_node()

    def exec_restore_pre_job(self):
        log.info('Enter restore cluster instance pre')
        self.my_base.write_progress_to_file(SubJobStatusEnum.RUNNING.value, ProgressConst.MIN_PROGRESS.value,
                                            "", SQLServerProgressFileType.COMMON)
        if not self.maintenance_mode_on(True):
            self.my_base.write_progress_to_file(SubJobStatusEnum.FAILED.value, 100,
                                                "", SQLServerProgressFileType.COMMON)
            return False
        self.my_base.exec_restore_pre_job()
        log.info('Leave restore cluster instance pre')
        return True

    def exec_sub_job(self):
        log.info('Enter restore_cluster_instance')
        self.my_base.exec_sub_job()
        log.info('Leave restore_cluster_instance')
        return True

    def exec_restore_post(self):
        log.info('Enter restore_cluster_post')
        self.my_base.write_progress_to_file(SubJobStatusEnum.FAILED.value, ProgressConst.MIN_PROGRESS.value,
                                            "", SQLServerProgressFileType.COMMON)
        if not self.maintenance_mode_on(False):
            self.my_base.write_progress_to_file(SubJobStatusEnum.FAILED.value, 100,
                                                "", SQLServerProgressFileType.COMMON)
            return False
        if not self.start_cluster_resources():
            log.error("start cluster resources error")
            return False
        try:
            self.my_base.exec_restore_post()
        except Exception as e_info:
            self.my_base.write_progress_to_file(SubJobStatusEnum.FAILED.value, 100,
                                                "", SQLServerProgressFileType.COMMON)
            return False
        log.info('Leave restore_cluster_post')
        return True

    def report_progress_comm(self):
        log.info('Enter process_report')
        try:
            self.my_base.report_progress_comm()
        except Exception as e:
            log.error(f'Get a exception from post msg = {str(e)}')
            return False
        log.info('Leave process_report')
        return True
